/*
 * @(#)GetRelatedDocumentsRunner.java
 * Date 2013-09-08
 * Version 1.0
 * Author Jim Horner
 * Copyright (c)2012
 */


package us.hornerscorners.lamppost.registry.query.es;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;

import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;

import us.hornerscorners.lamppost.exception.ServiceActionException;

import us.hornerscorners.lamppost.ihecommon.config.IHEConfig;

import us.hornerscorners.lamppost.ihecommon.service.ElasticSearchSource;

import us.hornerscorners.lamppost.model.ChainFilter;

import us.hornerscorners.lamppost.model.common.PersistableModelType;

import us.hornerscorners.lamppost.model.document.Association;
import us.hornerscorners.lamppost.model.document.DocumentEntry;

import us.hornerscorners.lamppost.registry.constant.AssociationType;
import us.hornerscorners.lamppost.registry.constant.QueryField;
import us.hornerscorners.lamppost.registry.constant.QuerySlotName;

import us.hornerscorners.lamppost.registry.model.QueryRequest;
import us.hornerscorners.lamppost.registry.model.QueryResponse;
import us.hornerscorners.lamppost.registry.model.RegistryObjects;

import us.hornerscorners.lamppost.registry.query.AssociationSourceModelFilter;
import us.hornerscorners.lamppost.registry.query.AssociationSourceOrTargetURNFilter;
import us.hornerscorners.lamppost.registry.query.AssociationTargetModelFilter;
import us.hornerscorners.lamppost.registry.query.AssociationTypeFilter;

import us.hornerscorners.lamppost.registry.transformer.model.CodedElementTransformer;
import us.hornerscorners.lamppost.registry.transformer.model.PatientIdentifierTransformer;

import us.hornerscorners.lamppost.registry.utils.RegistryObjectsUtils;


/**
 * Class description
 *
 *
 * @version        v1.0, 2013-09-08
 * @author         Jim Horner
 */
public class GetRelatedDocumentsRunner extends AbstractQueryRunner {

    /**
     * Constructs ...
     *
     *
     * @param config
     * @param elasticSearchSource
     * @param codedElementTransformer
     * @param patientIdentifierTransformer
     */
    public GetRelatedDocumentsRunner(
            IHEConfig config, ElasticSearchSource elasticSearchSource,
            CodedElementTransformer codedElementTransformer,
            PatientIdentifierTransformer patientIdentifierTransformer) {

        super(config, elasticSearchSource, codedElementTransformer,
              patientIdentifierTransformer);

    }

    /**
     * Method description
     *
     *
     * @param request
     *
     * @return
     */
    @Override
    public Map<PersistableModelType, QueryBuilder> createQuery(QueryRequest request) {

        Map<PersistableModelType, QueryBuilder> result = new HashMap<>();

        BoolQueryBuilder query = QueryBuilders.boolQuery();

        query.minimumNumberShouldMatch(1);

        result.put(PersistableModelType.DocumentEntry, query);

        BoolQueryBuilder query2 = QueryBuilders.boolQuery();

        query2.must(
            createStringQuery(
                request, QuerySlotName.AssociationTypes,
                QueryField.DocumentEntryParentDocumentAssociationsType));

        query2.minimumNumberShouldMatch(1);

        query.should(query2);

        if (request.hasValues(QuerySlotName.XDSDocumentEntryUniqueId)) {

            query.should(createUniqueIdentifierQuery(request,
                    QuerySlotName.XDSDocumentEntryUniqueId,
                    QueryField.DocumentEntryUniqueIdRoot,
                    QueryField.DocumentEntryUniqueIdExtension));

            query2.should(createUniqueIdentifierQuery(request,
                    QuerySlotName.XDSDocumentEntryUniqueId,
                    QueryField.DocumentEntryParentDocumentSourceUniqueIdRoot,
                    QueryField.DocumentEntryParentDocumentSourceUniqueIdExtension));

            query2.should(createUniqueIdentifierQuery(request,
                    QuerySlotName.XDSDocumentEntryUniqueId,
                    QueryField.DocumentEntryParentDocumentTargetUniqueIdRoot,
                    QueryField.DocumentEntryParentDocumentTargetUniqueIdExtension));
        }

        if (request.hasValues(QuerySlotName.XDSDocumentEntryEntryUUID)) {

            query.should(this.createStringQuery(request,
                    QuerySlotName.XDSDocumentEntryEntryUUID,
                    QueryField.DocumentEntryEntryUUID));

            query2.should(this.createStringQuery(request,
                    QuerySlotName.XDSDocumentEntryEntryUUID,
                    QueryField.DocumentEntryParentDocumentSourceEntryURN));

            query2.should(this.createStringQuery(request,
                    QuerySlotName.XDSDocumentEntryEntryUUID,
                    QueryField.DocumentEntryParentDocumentTargetEntryURN));
        }

        return result;
    }

    /**
     * Method description
     *
     *
     * @param request
     *
     * @return
     *
     * @throws ServiceActionException
     */
    @Override
    public QueryResponse run(QueryRequest request) throws ServiceActionException {

        QueryResponse result = super.run(request);

        RegistryObjects rol = result.getRegistryObjects();

        List<String> urns = new ArrayList<>();

        if (request.hasValues(QuerySlotName.XDSDocumentEntryUniqueId)) {

            String oid =
                request.getFirstParameterValue(QuerySlotName.XDSDocumentEntryUniqueId);

            for (DocumentEntry entry : rol.getDocumentEntries()) {

                if (oid.equals(entry.getUniqueId().getRoot())) {

                    urns.add(entry.getEntryURN());

                    break;
                }
            }

        } else {

            urns.add(
                request.getFirstParameterValue(QuerySlotName.XDSDocumentEntryEntryUUID));
        }

        List<AssociationType> types = new ArrayList<>();

        for (String assocType :
                request.getFirstSlotValues(QuerySlotName.AssociationTypes)) {

            AssociationType type = AssociationType.toAssociationType(assocType);

            if (type != null) {
                types.add(type);
            }
        }

        ChainFilter<Association> filter = new ChainFilter<>();

        filter.add(new AssociationSourceOrTargetURNFilter(urns));
        filter.add(new AssociationTypeFilter(types));
        filter.add(new AssociationSourceModelFilter(PersistableModelType.DocumentEntry));
        filter.add(new AssociationTargetModelFilter(PersistableModelType.DocumentEntry));

        rol.addAllAssociations(RegistryObjectsUtils.pullAllAssociations(rol, filter));

        if (ArrayUtils.isEmpty(rol.getAssociations())) {

            // then we return nothing
            rol.clear();
        }

        return result;
    }
}
